/*******************************************************************************
 * Copyright 2010 Simon Mieth
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package org.kabeja.ui.impl;

import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;

import org.kabeja.DraftDocument;
import org.kabeja.parser.Parser;
import org.kabeja.processing.ProcessPipeline;
import org.kabeja.processing.ProcessingManager;
import org.kabeja.tools.IOUtils;
import org.kabeja.ui.ApplicationMenuBar;
import org.kabeja.ui.ApplicationToolBar;
import org.kabeja.ui.DraftDocumentViewComponent;
import org.kabeja.ui.PropertiesEditor;
import org.kabeja.ui.PropertiesListener;
import org.kabeja.ui.ServiceManager;
import org.kabeja.ui.Serviceable;
import org.kabeja.ui.UIException;
import org.kabeja.ui.ViewComponent;
import org.kabeja.ui.event.DraftDocumentChangeEventProvider;
import org.kabeja.ui.event.DraftDocumentChangeListener;

import de.miethxml.toolkit.ui.PanelFactory;
import de.miethxml.toolkit.ui.UIUtils;

public class ProcessingRunViewComponent implements ViewComponent, Serviceable,
		ActionListener, PropertiesListener, DraftDocumentChangeEventProvider {
	protected JTabbedPane tabbedPane;
	protected JComponent view;
	protected JPanel pipelinePanel;
	protected boolean locked = false;
	protected boolean initialized = false;
	protected List viewComponents = new ArrayList();
	protected String processingPipeline;
	protected JTextArea logView;
	protected ProcessingManager manager;
	protected String baseDir = "./samples/dxf";
	protected File sourceFile;
	protected DraftDocument doc;
	protected boolean autogenerateOutput = false;
	protected Map properties = new HashMap();
	protected ArrayList listeners = new ArrayList();

	public String getTitle() {
		return "Run Processing";
	}

	public JComponent getView() {
		this.initialize();

		return this.view;
	}

	protected void initialize() {
		if (!this.initialized) {
			JPanel panel = PanelFactory
					.createTitledPanel(
							new JPanel(),
							Messages
									.getString("ProcessingRunViewComponent.processing.pipeline"),
							new ImageIcon(UIUtils.resourceToBytes(this
									.getClass(), "/icons/project.gif")));
			pipelinePanel = new JPanel(new GridLayout(0, 1, 0, 3));

			pipelinePanel
					.setBorder(BorderFactory.createEmptyBorder(4, 5, 4, 5));

			JScrollPane scroll = new JScrollPane(pipelinePanel);
			scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
			panel.add(scroll);

			JSplitPane sp = PanelFactory.createOneTouchSplitPane();
			sp.setLeftComponent(panel);
			tabbedPane = new JTabbedPane();
			tabbedPane.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));

			sp.setRightComponent(tabbedPane);
			sp.setDividerLocation(150);

			JSplitPane sp2 = PanelFactory
					.createOneTouchSplitPane(JSplitPane.VERTICAL_SPLIT);
			sp2.setTopComponent(sp);

			this.logView = new JTextArea();
			scroll = new JScrollPane(this.logView);
			scroll.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));

			JPanel p = PanelFactory.createTitledPanel(scroll, Messages
					.getString("ProcessingRunViewComponent.processing.output"));
			sp2.setBottomComponent(p);
			sp2.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

			sp2.setDividerLocation(400);
			this.view = sp2;

			DraftFileTransferHandler handler = new DraftFileTransferHandler(this);
			this.view.setTransferHandler(handler);
			this.initialized = true;
		}
	}

	public void setProcessingManager(ProcessingManager manager) {
		this.manager = manager;
		this.initialize();
		// setup the pipelines
		pipelinePanel.removeAll();

		Iterator i = manager.getProcessPipelines().keySet().iterator();

		while (i.hasNext()) {
			String pipelineName = (String) i.next();
			ProcessPipeline p = manager.getProcessPipeline(pipelineName);
			JButton button = new JButton(pipelineName);
			button.setActionCommand(pipelineName);
			button.addActionListener(this);
			button.setToolTipText(p.getDescription());
			button.setPreferredSize(button.getPreferredSize());
			pipelinePanel.add(button);
		}

		pipelinePanel.repaint();
	}

	public void setServiceManager(ServiceManager manager) {
		Object[] objects = manager
				.getServiceComponents(ApplicationToolBar.SERVICE);

		Action action = new AbstractAction(Messages
				.getString("ProcessingRunViewComponent.open.draft"),
				new ImageIcon(UIUtils.resourceToBytes(this.getClass(),
						"/icons/open.gif"))) {
			private static final long serialVersionUID = 1L;

			public void actionPerformed(ActionEvent e) {
				Runnable r = new Runnable() {
					public void run() {
						chooseInput();
					}
				};

				Thread t = new Thread(r);
				t.start();
			}
		};

		// we set the action to all toolbars if there are more then on
		for (int i = 0; i < objects.length; i++) {
			((ApplicationToolBar) objects[i]).addAction(action);
		}

		objects = manager.getServiceComponents(PropertiesEditor.SERVICE);

		for (int i = 0; i < objects.length; i++) {
			((PropertiesEditor) objects[i]).addPropertiesListener(this);
		}

		// view menu
		ApplicationMenuBar mbar = ((ApplicationMenuBar) manager
				.getServiceComponents(ApplicationMenuBar.SERVICE)[0]);
		JMenu viewMenu = new JMenu(Messages
				.getString("ProcessingRunViewComponent.process.view.menuitem"));

		if (!mbar.hasMenu(ApplicationMenuBar.MENU_ID_VIEW)) {
			mbar.setMenu(ApplicationMenuBar.MENU_ID_VIEW, new JMenu(Messages
					.getString("menu.view")));
		}

		objects = manager
				.getServiceComponents(DraftDocumentViewComponent.SERVICE);

		for (int i = 0; i < objects.length; i++) {
			ViewControl c = new ViewControl(
					(DraftDocumentViewComponent) objects[i]);
			this.viewComponents.add(c);

			JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(c.getTitle());
			menuItem.setSelected(c.isEnabled());
			menuItem.addItemListener(c);
			viewMenu.add(menuItem);
		}

		mbar.setJMenuItem(ApplicationMenuBar.MENU_ID_VIEW, viewMenu);
		mbar.setAction(ApplicationMenuBar.MENU_ID_FILE, action);
	}

	public void actionPerformed(ActionEvent e) {
		if (!this.locked) {
			this.processingPipeline = e.getActionCommand();
			this.locked = true;

			Runnable r = new Runnable() {
				public void run() {
					process();
				}
			};

			Thread t = new Thread(r);
			t.start();
		}
	}

	protected void process() {
		try {
			if (this.sourceFile.isDirectory()) {
				File[] files = this.sourceFile.listFiles();
				
				// we have to parse every file
				for (int i = 0; i < files.length; i++) {
				
					this.processFile(files[i]);
				
				}

				// file already parsed
			} else if (this.sourceFile.isFile()) {
				this.processFile(this.sourceFile);
			}
		} catch (Exception e) {
			this.logException(e);
		}

		this.locked = false;
	}

	protected void processFile(File f) {
		String ext = f.getAbsolutePath();
		int index = ext.lastIndexOf(".");

		if ((index + 1) < ext.length()) {
			ext = ext.substring(index + 1);

			try {
				this.logView.append(Messages
						.getString("ProcessingRunViewComponent.log.processing")
						+ f.getAbsolutePath() + "\n");

				File out = null;
				if (this.autogenerateOutput) {
					out = new File(f.getAbsolutePath().substring(0, index)
							+ "."
							+ this.manager.getProcessPipeline(
									this.processingPipeline).getGenerator()
									.getSuffix());

				} else {
					JFileChooser fc = new JFileChooser(this.baseDir);
					int value = fc.showSaveDialog(null);

					if (value == JFileChooser.APPROVE_OPTION) {
						out = fc.getSelectedFile();
					}
				}
				this.manager.process(new FileInputStream(f), ext,
						new HashMap(), this.processingPipeline,
						new FileOutputStream(out));

				this.manager.process(doc, this.properties,
						this.processingPipeline, new FileOutputStream(out));
				this.log(Messages
						.getString("ProcessingRunViewComponent.log.finished")
						+ out.getAbsolutePath() + "\n");

			} catch (Exception e) {
				e.printStackTrace();
				this.logException(e);
			}
		}

	}

	protected void chooseInput() {
		JFileChooser fc = new JFileChooser(this.baseDir);
		fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

		JCheckBox cb = new JCheckBox();
		cb.setSelected(true);

		JPanel p = new JPanel(new FlowLayout());
		p.add(cb);
		p
				.add(new JLabel(
						Messages
								.getString("ProcessingRunViewComponent.file.dialog.autogenerate")));
		fc.setAccessory(p);

		int value = fc.showOpenDialog(null);

		if (value == JFileChooser.APPROVE_OPTION) {
			File file = fc.getSelectedFile();
			this.autogenerateOutput = cb.isSelected();

			if (file.isFile()) {
				this.baseDir = file.getParent();
				
				this.processInput(file);
			} else if (file.isDirectory()) {
				this.baseDir = file.getAbsolutePath();
				this
						.log(Messages
								.getString("ProcessingRunViewComponent.log.select.directory")
								+ file.getAbsolutePath() + "\n");
				this.log("No preview\n");
			}
		}
	}



	protected void propagateDraftDocument(DraftDocument doc) throws Exception {
		Iterator i = this.viewComponents.iterator();

		while (i.hasNext()) {
			ViewControl c = (ViewControl) i.next();

			if (c.isEnabled()) {
				c.getDraftDocumentViewComponent().showDraftDocument(doc);
			}
		}
	}

	protected void logException(Exception e) {
		StringWriter sw = new StringWriter();
		e.printStackTrace(new PrintWriter(sw));
		this.log(sw.toString());
	}

	protected void log(String msg) {
		this.logView.append(msg);
		this.logView.setCaretPosition(this.logView.getDocument().getLength());
	}

	public void propertiesChanged(Map props) {
		// copy changed properties to my properties
		Iterator i = props.keySet().iterator();

		while (i.hasNext()) {
			String key = (String) i.next();
			this.properties.put(key, props.get(key));
		}
	}

	public void addDraftDocumentChangeListener(DraftDocumentChangeListener listener) {
		this.listeners.add(listener);
	}

	public void removeDraftDocumentChangeListener(
			DraftDocumentChangeListener listener) {
		this.listeners.remove(listener);
	}

	protected void fireDraftDocumentChangeEvent() throws Exception {
		Iterator i = ((ArrayList) this.listeners.clone()).iterator();

		while (i.hasNext()) {
			DraftDocumentChangeListener l = (DraftDocumentChangeListener) i.next();

			if (!(l instanceof DraftDocumentViewComponent)) {
				// avoid double event
				l.changed(this.doc);
			}
		}

		this.propagateDraftDocument(this.doc);
	}

	public void processInput(File file) {
	 if(file.isFile()){

		try {
            String extension = IOUtils.getFileExtension(file);
			Parser p = this.manager.getParser(extension);
			this.doc = p.parse(new FileInputStream(file),new HashMap());
			this.fireDraftDocumentChangeEvent();
		} catch (Exception e) {
			e.printStackTrace();
			this.logException(e);
		}
	 }
	}

	private class ViewControl implements ItemListener {
		private JComponent view;
		private String title;
		private DraftDocumentViewComponent component;
		private int index;

		ViewControl(DraftDocumentViewComponent component) {
			this.component = component;
			this.view = component.getView();
			this.index = tabbedPane.getTabCount();
			tabbedPane.add(component.getTitle(), view);
		}

		public boolean isEnabled() {
			return this.view.isEnabled();
		}

		public void setEnabled(boolean b) {
			this.view.setEnabled(b);
			this.enableChildren(b, this.view);
			tabbedPane.setEnabledAt(this.index, b);

			if (b && (doc != null)) {
				try {
					this.component.showDraftDocument(doc);
				} catch (UIException e) {
					e.printStackTrace();
				}
			}
		}

		public DraftDocumentViewComponent getDraftDocumentViewComponent() {
			return this.component;
		}

		public String getTitle() {
			return this.component.getTitle();
		}

		public void itemStateChanged(ItemEvent e) {
			if (e.getStateChange() == ItemEvent.SELECTED) {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						setEnabled(true);
					}
				});
			} else {
				SwingUtilities.invokeLater(new Runnable() {
					public void run() {
						setEnabled(false);
					}
				});
			}
		}

		protected void enableChildren(boolean b, Container c) {
			for (int i = 0; i < c.getComponentCount(); i++) {
				Component comp = c.getComponent(i);
				comp.setEnabled(b);

				if (comp instanceof Container) {
					enableChildren(b, (Container) comp);
				}
			}
		}
	}
}
